/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package plash.display;

import plash.events.Event;
import plash.events.EventDispatcher;
import plash.geom.Point;
import plash.geom.Rectangle;
import plash.utils.Math;
import processing.core.PApplet;
import processing.core.PGraphics;
import processing.core.PImage;

/**
 *
 * @author #0K Srinivasan <hashok.com>
 */
abstract class DisplayObject extends EventDispatcher implements IDrawable
{

    protected static int counter = 0;
    protected String __name = "instance" + DisplayObject.counter++;
    protected int __x = 0;
    protected int __y = 0;
    protected int __rotation = 0;
    protected int __width = 0;
    protected int __height = 0;
    protected int __mouseX = 0;
    protected int __mouseY = 0;
    protected float __alpha = 1.0f;
    protected float __radiansRotation = 0;
    protected float __scaleX = 1.0f;
    protected float __scaleY = 1.0f;
    protected boolean __visible = true;
    protected boolean __isMasker = false;
    protected boolean __hasMasker = false;
    protected Point __location = new Point();
    protected Rectangle __boundingBox = new Rectangle();
    protected DisplayObject __mask;
    protected DisplayObjectContainer __parent;
    protected PGraphics __painter;
    protected IStage __stage;

    public DisplayObject()
    {
        super();
    }

    @Override
    public void draw()
    {
        if (this.getStage() == null)
        {
            return;
        }
        if (this.getParent() == null)
        {
            return;
        }
        if (this.getPainter() == null)
        {
            return;
        }

        if (this.__isMasker)
        {
            this.__painter.endDraw();
        }

        if (this.__hasMasker)
        {
            this.getMask().draw();
//            this.__applyMask2();
            this.__painter.endDraw();
            this.__applyMask();
            this.__stage.getStagePainter().image(this.__painter, 0, 0);
        }
        dispatchEvent(new Event(Event.ENTER_FRAME));
    }

    public String getName()
    {
        return __name;
    }

    public void setName(String name)
    {
        this.__name = name;
    }

    public int getAbsoluteX()
    {
        if (this.getName().equals("_root"))
        {
            return this.__x;
        }
        else
        {
            if (this.getParent() != null)
            {
                return this.getParent().getAbsoluteX() + this.getX();
            }
            else
            {
                return this.getX();
            }
        }
    }

    public int getAbsoluteY()
    {
        if (this.getName().equals("_root"))
        {
            return this.__y;
        }
        else
        {
            if (this.getParent() != null)
            {
                return this.getParent().getAbsoluteY() + this.getY();
            }
            else
            {
                return this.getY();
            }
        }
    }

    public int getX()
    {
        return __x;
    }

    public void setX(int x)
    {
        __location.setX(x);
        this.__x = x;
    }

    public int getY()
    {
        return __y;
    }

    public void setY(int y)
    {
        __location.setY(y);
        this.__y = y;
    }

    public int getRotation()
    {
        return __rotation;
    }

    public void setRotation(int rotation)
    {
        this.__rotation = rotation;
        this.__radiansRotation = Math.toRadians(rotation);
    }

    public int getWidth()
    {
        return __width;
    }

    public void setWidth(int width)
    {
        this.__width = width;
    }

    public int getHeight()
    {
        return __height;
    }

    public void setHeight(int height)
    {
        this.__height = height;
    }
    
    public float getAbsoluteAlpha()
    {
        if (this.__parent != null)
        {
            return this.__alpha * this.__parent.getAlpha();
        }
        return this.__alpha;
    }

    public float getAlpha()
    {
        return __alpha;
    }

    public void setAlpha(float alpha)
    {
        this.__alpha = alpha;
    }

    public int getMouseX()
    {
        if (this.getName().equals("_root"))
        {
            return this.__stage.getMouseX();
        }
        else
        {
            if (this.getParent() == null)
            {
                return 0;
            }
            else
            {
                return this.getParent().getMouseX() - this.getX();
            }
        }
    }

    public int getMouseY()
    {
        if (this.getName().equals("_root"))
        {
            return this.__stage.getMouseY();
        }
        else
        {
            if (this.getParent() == null)
            {
                return 0;
            }
            else
            {
                return this.getParent().getMouseY() - this.getY();
            }
        }
    }

    public boolean isVisible()
    {
        return __visible;
    }

    public void setVisible(boolean visible)
    {
        this.__visible = visible;
    }

    public DisplayObjectContainer getParent()
    {
        return __parent;
    }

    public void setParent(DisplayObjectContainer parent)
    {
        this.__parent = parent;
        if (this.__parent != null)
        {
            dispatchEvent(new Event(Event.ADDED));
        }
        else
        {
            dispatchEvent(new Event(Event.REMOVED));
        }
    }

    public PGraphics getPainter()
    {
        return this.__painter;
    }

    public void setPainter(PGraphics painter)
    {
        this.__painter = painter;
    }
    
    public float getAbsoluteScaleX()
    {
        if(this.__parent != null)
        {
            return this.__scaleX * this.__parent.getAbsoluteX();
        }
        return this.__scaleX;
    }

    public float getScaleX()
    {
        return __scaleX;
    }

    public void setScaleX(float scaleX)
    {
        this.__scaleX = scaleX;
    }
    
    public float getAbsoluteScaleY()
    {
        if(this.__parent != null)
        {
            return this.__scaleY * this.__parent.getAbsoluteY();
        }
        return this.__scaleY;
    }

    public float getScaleY()
    {
        return __scaleY;
    }
    public void setScaleY(float scaleY)
    {
        this.__scaleY = scaleY;
    }

    public Rectangle getBoundingBox()
    {
        _updateBoundingBox();
        return __boundingBox;
    }

    public void setLocation(Point p)
    {
        this.setX(p.getX());
        this.setY(p.getY());
    }

    public void setLocation(int x, int y)
    {
        this.setX(x);
        this.setY(y);
    }

    public Point getLocation()
    {
        __location.setX(__x);
        __location.setY(__y);
        return __location;
    }

    public DisplayObject getMask()
    {
        return __mask;
    }

    public void setMask(DisplayObject mask)
    {
        //remove existing masker
        if (__mask != null)
        {
            __mask.setIsMasker(false);
        }

        __mask = mask;
        __hasMasker = (this.__mask != null);

        if (__mask != null)
        {
            __mask.setIsMasker(true);
        }

        if (this.__stage != null)
        {
            if (__hasMasker)
            {
                this.setPainter(this.__stage.getMaskablePainter());
            }
        }
    }

    public void setIsMasker(boolean isMasker)
    {
        this.__isMasker = isMasker;

        if (this.__stage != null)
        {
            if (this.__isMasker)
            {
                this.setPainter(this.__stage.getMaskingPainter());
            }
        }
    }

    public boolean isMasker()
    {
        return __isMasker;
    }

    public boolean hasMasker()
    {
        return __hasMasker;
    }

    public void setHasMasker(boolean hasMasker)
    {
        this.__hasMasker = hasMasker;
    }

    public IStage getStage()
    {
        return __stage;
    }

    public void setStage(IStage stage)
    {
        this.__stage = stage;
        
        this.setMask(__mask);
        this.setIsMasker(__isMasker);
        
        if (stage != null)
        {
            dispatchEvent(new Event(Event.ADDED_TO_STAGE));
        }
        else
        {
            dispatchEvent(new Event(Event.REMOVED_FROM_STAGE));
        }
    }

    protected void __applyMask()
    {
        PImage src = this.getPainter();
        int[] maskArray = this.getMask().getPainter().pixels;

        if ((src == null) || (maskArray == null))
        {
            return;
        }
        src.loadPixels();
        // don't execute if mask image is different size
        if (maskArray.length != src.pixels.length)
        {
            throw new RuntimeException("mask() can only be used with an image that's the same size.");
        }
        for (int i = 0; i < src.pixels.length; i++)
        {
            if (((maskArray[i] >> 24) & 0xFF) == 0x00)
            {
                src.pixels[i] = 0;
            }
        }
        src.format = PApplet.ARGB;
        src.updatePixels();
    }
    
    protected void __applyMask2()
    {
        if (this.__mask == null)
        {
            return;
        }

        PGraphics mask = this.__mask.getPainter();
        
        Rectangle srcBounds = this.getBoundingBox();
        Rectangle maskBounds = this.__mask.getBoundingBox();
        Rectangle srcMaskIntersection = srcBounds.intersection(maskBounds);
        Rectangle stageRectangle = this.getStage().getStageSize();
        Rectangle renderingRectangle;
        
        if (srcMaskIntersection.isEmpty())
        {
            if(maskBounds.containsRectangle(srcBounds))
            {
                srcMaskIntersection = srcBounds;
            }
            else if(srcBounds.containsRectangle(maskBounds))
            {
                srcMaskIntersection = maskBounds;
            }
            else 
            {
                this.__painter.background(0, 0, 0, 0);
                return;
            }
        }
        
        if (stageRectangle.containsRectangle(srcMaskIntersection))
        {
            renderingRectangle = srcMaskIntersection;
        }
        else
        {
            if (stageRectangle.intersects(srcMaskIntersection))
            {
                renderingRectangle = stageRectangle.intersection(srcMaskIntersection);
            }
            else
            {
                this.__painter.background(0, 0, 0, 0);
                return;
            }
        }
        System.out.println("-------------------------------------------");
        System.out.println("SOURCE RECTANGLE "+srcBounds);
        System.out.println("MASK RECTANGLE "+maskBounds);
        System.out.println("STAGE RECTANGLE "+stageRectangle);
        System.out.println("INTERSECTION SOURCE AND MASK "+srcMaskIntersection);
        System.out.println("RENDERING RECTANGLE "+renderingRectangle);
        
        
        this.getStage().getTempPainter().beginDraw();
        this.getStage().getTempPainter().background(0, 0, 0, 0);
        
        for (int i = 0; i <= renderingRectangle.getWidth(); i++)
        {
            for (int j = 0; j <= renderingRectangle.getHeight(); j++)
            {
                int argbSrc = this.__painter.get(i, j);
                
                if (((argbSrc >> 24) & 0xFF) != 0x00)
                {
                    int argbMask = mask.get(i, j);
                    if (((argbMask >> 24) & 0xFF) != 0x00)
                    {
                        this.getStage().getTempPainter().set(i, j, argbSrc);
                    }
                }
            }
        }
        
//        for (int i = renderingRectangle.getX(); i <= renderingRectangle.getRight(); i++)
//        {
//            for (int j = renderingRectangle.getY(); j <= renderingRectangle.getBottom(); j++)
//            {
//                int argbSrc = this.__painter.get(i, j);
//                
//                if (((argbSrc >> 24) & 0xFF) != 0x00)
//                {
//                    int argbMask = mask.get(i, j);
//                    if (((argbMask >> 24) & 0xFF) != 0x00)
//                    {
//                        this.getStage().getTempPainter().set(i, j, argbSrc);
//                    }
//                }
//            }
//        }
        
        this.getStage().getTempPainter().endDraw();
        this.__painter.background(0, 0, 0, 0);
        this.__painter.image(
                this.getStage().getTempPainter(),0,0);
    }

    protected void _updateBoundingBox()
    {
        __boundingBox = new Rectangle(0, 0, 100, 100);
    }
}
